 1.Apache Kylin 实战之Cube优化
   
   1).Cuboid剪枝优化
   Cuboid 特指 Kylin 中在某一种维度组合下所计算的所有数据。
   以减少Cuboid数量为目的的优化统称为Cuboid剪枝。
   在没有采取任何优化措施的情况下，Kylin会对每一种维度的组合进行预计算。
       如果有4个维度，可能最终会有 2^4 =16个 Cuboid需要计算
	   如果有10个维度，那么没有经过任何优化的Cube就会存在 2^10 =1024 个
Cuboid
       如果有20个维度，那么Cube中总共会存在 2^20 = 1,048,576 个 Cuboid
   过多的 Cuboid 数量对构建引擎、存储引擎压力非常巨大。因此，在构建维度数量较
多的Cube时，尤其要注意Cube的剪枝优化。
   Cube的剪枝优化是一种试图减少额外空间占用的方法，这种方法的前提是不会明显
影响查询时间。在做剪枝优化的时候：
       需要选择跳过那些“多余”的Cuboid
	   有的Cuboid因为查询样式的原因永远不会被查询到，因此显得多余
	   有的Cuboid的能力和其他Cuboid接近，因此显得多余
   Kylin提供了一系列简单的工具来帮助他们完成Cube的剪枝优化
   2).检查Cuboid数量
   Apache Kylin提供了一个简单的工具，检查Cube中哪些Cuboid最终被预计算了，称
这些Cuboid为被物化的Cuboid，该工具还能给出每个Cuboid所占空间的估计值。由于
该工具需要在对数据进行一定阶段的处理之后才能估算Cuboid的大小，一般来说在Cube
构建完毕之后再使用该工具

   使用如下的命令行工具去检查这个Cube中的Cuboid状态：
kylin.sh org.apache.kylin.engine.mr.common.CubeStatsReader lagou_sales_cube4

============================================================================
Statistics of lagou_sales_cube4[20201001000000_20201005000000]

Cube statistics hll precision: 14
Total cuboids: 15
Total estimated rows: 254
Total estimated size(MB): 0.0034487247467041016
Sampling percentage:  100
Mapper overlap ratio: 0.0
Mapper number: 0
Length of dimension LAGOU_KYLIN.DW_SALES1.DT is 1
Length of dimension LAGOU_KYLIN.DW_SALES1.PRODUCTID is 1
Length of dimension LAGOU_KYLIN.DW_SALES1.CHANNELID is 1
Length of dimension LAGOU_KYLIN.DW_SALES1.REGIONID is 1
|---- Cuboid 1111, est row: 72, est MB: 0
    |---- Cuboid 0111, est row: 18, est MB: 0, shrink: 25%
        |---- Cuboid 0011, est row: 6, est MB: 0, shrink: 33.33%
            |---- Cuboid 0001, est row: 2, est MB: 0, shrink: 33.33%
            |---- Cuboid 0010, est row: 3, est MB: 0, shrink: 50%
        |---- Cuboid 0101, est row: 8, est MB: 0, shrink: 44.44%
            |---- Cuboid 0100, est row: 4, est MB: 0, shrink: 50%
        |---- Cuboid 0110, est row: 9, est MB: 0, shrink: 50%
    |---- Cuboid 1011, est row: 24, est MB: 0, shrink: 33.33%
        |---- Cuboid 1001, est row: 8, est MB: 0, shrink: 33.33%
            |---- Cuboid 1000, est row: 4, est MB: 0, shrink: 50%
        |---- Cuboid 1010, est row: 12, est MB: 0, shrink: 50%
    |---- Cuboid 1101, est row: 32, est MB: 0, shrink: 44.44%
        |---- Cuboid 1100, est row: 16, est MB: 0, shrink: 50%
    |---- Cuboid 1110, est row: 36, est MB: 0, shrink: 50%
   输出结果分析：
Cube statistics hll precision: 14
Total cuboids: 15
Total estimated rows: 203
Total estimated size(MB): 0.0027539730072021484
Sampling percentage: 100
Mapper overlap ratio: 0.0
Mapper number: 0

   估计Cuboid大小的精度（Hll Precision）
   总共的Cuboid数量
   Segment的总行数估计
   Segment的大小估计，Segment的大小决定mapper、reducer的数量、数据分
片数量等
   
   所有的 Cuboid 及它的分析结果都以树状的形式打印了出来
   在这棵树中，每个节点代表一个Cuboid，每个Cuboid都由一连串1或0的数字组成
   数字串的长度等于有效维度的数量，从左到右的每个数字依次代表Rowkeys设置中
的各个维度。如果数字为0，则代表这个Cuboid中不存在相应的维度；如果数字为1,
则代表这个Cuboid中存在相应的维度
   除了最顶端的Cuboid之外，每个Cuboid都有一个父亲Cuboid，且都比父亲Cuboid
少了一个“1”。其意义是这个Cuboid就是由它的父亲节点减少一个维度聚合而来的
(上卷）
   最顶端的Cuboid称为Base Cuboid，它直接由源数据计算而来。Base Cuboid中
包含所有的维度，因此它的数字串中所有的数字均为1
   每行Cuboid的输出中除了0和1的数字串以外，后面还有每个Cuboid的具体信息,
包括该Cuboid行数的估计值、该Cuboid大小的估计值，以及这个Cuboid的行数与
父亲节点的对比（Shrink值）
   所有Cuboid行数的估计值之和应该等于Segment的行数估计值，所有Cuboid的
大小估计值应该等于该Segment的大小估计值。每个Cuboid都是在它的父亲节点的
基础上进一步聚合而成的
   3).检查Cube大小
   在Web GUI的Model页面选择一个READY状态的Cube，光标移到该Cube的Cube Size
列时，Web GUI会提示Cube的源数据大小，以及当前Cube的大小除以源数据大小的比
例，称为膨胀率（Expansion Rate）
   查看cube大小
   一般来说，Cube的膨胀率应该在0%~1000%之间，如果一个Cube的膨胀率超过
1000%,那么应当查找其中的原因。膨胀率高可能有以下几个方面的原因：
       Cube中的维度数量较多，且没有进行很好的Cuboid剪枝优化，导致Cuboid数量
极多
       Cube中存在较高基数的维度（基数的维度是指维度中有多少个不同的值），导
致包含这类维度的每一个Cuboid占用的空间都很大，这些Cuboid累积造成整体Cube
体积变大
       存在占用空间大的度量。例如Count Distinct，因此需要在Cuboid的每一行中都
为其保存一个较大度量数据，最坏的情况将会导致Cuboid中每一行都有数十KB,从而
造成整个Cube的体积变大

   对于Cube膨胀率居高不下的情况，需要结合实际数据进行分析，优化。
   4).使用衍生维度
   一个维度可以是普通维度或者是衍生维度(Derived)。
   将维度表的维度设置为衍生维度，这个维度不会参与预计算，而是使用维度表的主键
(或事实表的外键)来替代它。
   Kylin会在底层记录维表主键与维度表其他维度之间的映射关系，以便在查询时能够
动态地将维度表的主键翻译成这些非主键维度，并进行实时聚合。
   创建Cube的时候，这些维度如果指定为衍生维度，Kylin将会排除这些维度，而是使
用维度表的主键来代替它们创建Cuboid。后续查询的时候，再基于主键的聚合结果,再
进行一次聚合。
   使用衍生维度会有效减少Cube中 Cuboid 的数量；但在查询时会增加聚合的时间。
   不适用的场景：
       如果从维度表主键到某个维度表维度所需要的聚合工作量非常大，此时作为一个
普通的维度聚合更合适，否则会影响Kylin的查询性能
   案例1 - 定义衍生维度及对比
   有以下时间日期维表：
   类型      示例值 	  		列名 		   注释
   string    2020-01-01 		dateid 	       年-月-日
   string    2020               dayofyear      年份。备注不能使用：year
   string    1                  dayofmonth     月份。备注不能使用：month
   string    1                  day_in_year    当年的第几天
   string    1                  day_in_month   当月的第几天
   string    星期一             weekday        星期
   string    1                  week_in_month  当月的第几个星期
   string    1                  week_in_year   当年的第几个星期
   string    workday、weekend、 date_type      工作日：workday 国家法定节假日： 
             holiday                           holiday 休息日：weekend
   string    Q1                 quarter 	   季度
   
   -- 建表
drop table lagou_kylin.dim_date;
create table lagou_kylin.dim_date(
dateid string,
dayofyear string,
dayofmonth string,
day_in_year string,
day_in_month string,
weekday string,
week_in_month string,
week_in_year string,
date_type string,
quarter string
)ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';

-- 加载数据
LOAD DATA LOCAL INPATH '/root/kylin/dim_date.txt' OVERWRITE
INTO TABLE lagou_kylin.dim_date;

   -- 数据
2019-02-01,2019,2,32,1,5,1,5,workday,Q1
2019-02-02,2019,2,33,2,6,1,5,workday,Q1
   备注：日期维表dim_date中有两个字段：dayofyear、dayofmonth，不能是year、
month。
   如果是 year、month，定义 model、cube、build cube 都没问题，但是执行查询时
涉及到year、month两列会报错。
   (1).Cube设计
   select dim_date.dayofyear, sum(price)
from lagou_kylin.dw_sales join lagou_kylin.dim_date on
dw_sales.date1 = dim_date.dateid
group by dim_date.dayofyear;
   创建项目 => 指定数据源 => 定义 model => 定义Cube => 查询
   (2).加载数据源
   (3).定义model
   (4).定义Cube（不使用衍生维度）
   (5).定义Cube（使用衍生维度）
   (6).分别检查Cube的Cuboid数量
   kylin.sh org.apache.kylin.engine.mr.common.CubeStatsReader lagou_sales_cube5
============================================================================
Statistics of lagou_sales_cube5[FULL_BUILD]

Cube statistics hll precision: 14
Total cuboids: 1
Total estimated rows: 2
Total estimated size(MB): 1.3828277587890625E-5
Sampling percentage:  100
Mapper overlap ratio: 1.0
Mapper number: 1
Length of dimension LAGOU_KYLIN.DW_SALES.DATE1 is 1
|---- Cuboid 1, est row: 2, est MB: 0
----------------------------------------------------------------------------   
   kylin.sh org.apache.kylin.engine.mr.common.CubeStatsReader lagou_sales_cube6 
============================================================================
Statistics of lagou_sales_cube6[FULL_BUILD]

Cube statistics hll precision: 14
Total cuboids: 2047
Total estimated rows: 4031
Total estimated size(MB): 0.032235145568847656
Sampling percentage:  100
Mapper overlap ratio: 1.0
Mapper number: 1
Length of dimension LAGOU_KYLIN.DW_SALES.DATE1 is 1
Length of dimension LAGOU_KYLIN.DIM_DATE.DATEID is 1
Length of dimension LAGOU_KYLIN.DIM_DATE.DAYOFYEAR is 1
Length of dimension LAGOU_KYLIN.DIM_DATE.DAYOFMONTH is 1
Length of dimension LAGOU_KYLIN.DIM_DATE.DAY_IN_YEAR is 1
Length of dimension LAGOU_KYLIN.DIM_DATE.DAY_IN_MONTH is 1
Length of dimension LAGOU_KYLIN.DIM_DATE.WEEKDAY is 1
Length of dimension LAGOU_KYLIN.DIM_DATE.WEEK_IN_MONTH is 1
Length of dimension LAGOU_KYLIN.DIM_DATE.WEEK_IN_YEAR is 1
Length of dimension LAGOU_KYLIN.DIM_DATE.DATE_TYPE is 1
Length of dimension LAGOU_KYLIN.DIM_DATE.QUARTER is 1
|---- Cuboid 11111111111, est row: 2, est MB: 0
    |---- Cuboid 00110001111, est row: 1, est MB: 0, shrink: 50%
----------------------------------------------------------------------------
      
   案例2 - 定义衍生维度及对比
   构建与前面Cube4类似的Cube7，仅在维度定义有区别，以下是二者的对比：
   Cube7 定义
   Cube4 分析结果
Cube statistics hll precision: 14
Total cuboids: 15
Total estimated rows: 254
Total estimated size(MB): 0.0034487247467041016
Sampling percentage:  100
Mapper overlap ratio: 0.0
Mapper number: 0
   Cube7 分析结果
Cube statistics hll precision: 14
Total cuboids: 127
Total estimated rows: 4444
Total estimated size(MB): 0.06186056137084961
Sampling percentage:  100
Mapper overlap ratio: 1.0
Mapper number: 1
   5).聚合组
   随着维度数目的增加，Cuboid 的数量会爆炸式地增长。为了缓解 Cube 的构建压力,
Apache Kylin 引入了一系列的高级设置，帮助用户筛选出真正需要的Cuboid(本质
是要减少Cube构建过程中的预计算)。这些高级设置包括：
       聚合组（Aggregation Group）
	   强制维度（Mandatory Dimension）
	   层级维度（Hierachy Dimension）
	   联合维度（Joint Dimension）
   默认 Kylin 会把所有维度放在同一个聚合组中。
   如果维度数较多（如维度数 > 15)，建议用户根据查询的习惯和模式，将维度分布
到多个聚合组中。通过使用多个聚合组，可以大大降低 Cube 中的 Cuboid 数量。
   如一个Cube有 (M+N) 个维度：
       这些维度放在一个聚合组中，默认有 2^(M+N) 个 Cuboid
	   将这些维度分为两个不相交的聚合组，第一个组有M个维度，第二个组有N个维
度。那么 Cuboid 的总数为：（ 2^M + 2^N ）个维度
       一个维度可以出现在多个聚合组中
   在单个聚合组中，可以对维度设置一些高级属性，包括强制维度、层级维度、联合维
度。一个维度只能出现在一个属性组中。
   构建 N 个维度的 Cube 会生成 2^N个 Cuboid。如下图所示，构建一个 4 个维度
（A，B，C, D）的 Cube，需要生成 16 个Cuboid。
   
   根据用户关注的维度组合，可以维度划分不同的组合类，这些组合类在 Kylin 中被称
为聚合组。如用户仅仅关注维度 AB 组合和维度 CD 组合，那么该 Cube 则可以被分化成
两个聚合组，分别是聚合组 AB 和聚合组 CD。生成的 Cuboid 数目从 16 个缩减为 8 个。
   
   用户关心的聚合组之间可能包含相同的维度。如聚合组 ABC 和聚合组 BCD 都包含维
度 B 和维度 C。这些聚合组之间会衍生出相同的 Cuboid。聚合组ABC会产生Cuboid BC，
聚合组 BCD 也会产生 Cuboid BC。这些 Cuboid不会重复生成，一份Cuboid为这些聚合组 
所共有。
   
   有了聚合组就可以粗粒度地对 Cuboid 进行筛选，获取自己想要的维度组合。
   Kylin的建模需要业务专家参与。
   6).强制维度(Mandatory Dimension)
   强制/必要 维度：指的是那些总会出现在 Where 条件或 Group By 语句中的维度。
   通过指定某些维度为强制维度，Kylin 不预计算那些不包含此维度的 Cuboid ，从而
减少计算量。
   维度A是强制维度，那么生成的 Cube 如下图所示，维度数目从16变为9。
   7).层级维度(Hierachy Dimension)
   层级维度：是指一组有层级关系的维度。
   维度中常常会出现具有层级关系的维度。例如国家、省份、城市这三个维度，从上而
下来说国家／省份／城市之间分别是一对多的关系。假设维度 A 代表国家，维度 B
代表省份，维度 C 代表城市，ABC 三个维度可以被设置为层级维度，生成的Cube 如
下图所示：
   
   Cuboid [A,C,D]=Cuboid[A, B, C, D]，Cuboid[B, D]=Cuboid[A, B, D]，因而
Cuboid[A, C, D] 和 Cuboid[B, D] 就不必重复存储。
   8).联合维度(Joint Dimension)
   联合维度：是将多个维度视作一个维度，在进行组合计算的时候，它们要么一起出
现，要么均不出现。通常适用于以下几种情形：
       总是在一起查询的维度
	   彼此之间有一定映射关系，如USER_ID和EMAIL
	   基数很低的维度。如性别、布尔类型的属性
   维度的基数：维度有多少个不同的值。
   联合维度并不关心维度之间各种细节的组合方式。如用户的查询语句中仅仅会出现
group by A, B, C ，而不会出现 group by A、B 或者 group by C 等等这些细化的
维度组合。这一类问题就是联合维度所解决的问题。
   例如将维度 A、B、C 定义为联合维度，Apache Kylin 就仅仅会构建 Cuboid ABC，
而 Cuboid AB、BC、A 等等Cuboid 都不会被生成。最终的 Cube 结果如下图所示,
Cuboid 数目从 16 减少到 4。
   
   小结：  
       在单个聚合组中，可以对维度进行设置，包括强制维度、层级维度、联合维度。
一个维度只能出现在一个属性组中
       强制维度：指的是那些总会出现在Where条件或Group By子句中的维度
	   层级维度：一组具有层级关系的维度(如：国家、省、市)
	   联合维度：将多个维度看成一个维度。要么一起出现、要么都不出现
   9).Rowkeys
   简单的说Cuboid的维度会映射为HBase的Rowkey，Cuboid的指标会映射为HBase
的Value。
   如上图原始表所示：Hive表有两个维度列 year 和 city ，有一个指标列 price 。
   如上图预聚合表所示：我们具体要计算的是year和city 这两个维度所有维度组合(即4
个cuboid)下的 sum(priece) 指标，这个指标的具体计算过程就是由MapReduce完成的。
   
   如上图字典编码所示：为了节省存储资源，Kylin对维度值进行了字典编码。图中将
beijing 和 shanghai 依次编码为0和1。
   如上图HBase KV存储所示：在计算cuboid过程中，会将Hive表的数据转化为HBase
的KV形式。Rowkey的具体格式是cuboid id + 具体的维度值(最新的Rowkey中为了并发
查询还加入了Shard Key),以预聚合表内容的第2行为例,其维度组合是(year,city),
所以cuboid id就是00000011，cuboid是8位，具体维度值是1994和shanghai,所以编码
后的维度值对应上图的字典编码也是11，所以HBase的Rowkey就是0000001111,对应的
HBase Value就是 sum(priece) 的具体值。
   
   所有的cuboid计算完成后，会将cuboid转化为HBase的 KeyValue 格式生成HBase的
HFile，最后将HFile load进cube对应的HBase表中。
   1).编码
   Kylin 以 Key-Value 的方式将 Cube 存储到 HBase 中，HBase 的 key就是 Rowkey，
是由各维度的值拼接而成的。为了更高效地存储这些值，Kylin 会对它们进行编码和
压缩；每个维度均可以选择合适的编码方式，默认采用的是字典（Dictionary）编码
技术。字段支持的基本编码类型如下：
       Dictionary ：字典编码将所有此维度下的值构成一张映射表，从而大大节约存
储空间,适用于大部分字段,默认推荐使用。Dictionary产生的编码非常紧凑,尤其在维度
的值基数小且长度大的情况下。但在超高基情况下,可能引起内存不足的问题,在Kylin中字
典编码允许的基数上限默认是500万(由参数kylin.dictionary.max.cardinality配置)
       boolean ：适用于字段值为 true, false, TRUE, FALSE, True, False, t, f, 
T, F, yes, no, YES, NO, Yes, No, y, n, Y, N, 1, 0
       integer ：适用于字段值为整数字符，支持的整数区间为 [ -2^(8N-1), 
2^(8N-1)]
       date ：适用于字段值为日期字符，支持的格式包括 yyyyMMdd、yyyy-MM-dd、
yyyy-MM-dd HH:mm:ss、yyyy-MM-dd HH:mm:ss.SSS
       time ：适用于字段值为时间戳字符，支持范围为 [1970-01-01 00:00:00,
2038/01/19 03:14:07] ，毫秒部分会被忽略，time编码适用于 time、datetime、
timestamp 等类型
       fix_length ：适用于超高基场景，将选取字段的前N个字节作为编码值,当 N
小于字段长度,会造成字段截断,当 N 较大时，造成 RowKey 过长，查询性能下降,只
适用于 varchar 或 nvarchar 类型
       fixed_length_hex ：适用于字段值为十六进制字符,比如 1A2BFF 或者FF00FF,
每两个字符需要一个字节，只适用于 varchar 或 nvarchar 类型
   2).顺序
   各维度在 Rowkeys 中的顺序，对于查询的性能会产生较明显的影响；在这里用户可
以根据查询的模式和习惯，通过拖曳的方式调整各个维度在Rowkeys上的顺序。推荐的
顺序为：
       Mandatory 维度
	   where 过滤条件中出现频率较多的维度
	   高基数维度
	   低基数维度放后面
	   不常用的维度放在后面
   3).分片
   指定 ShardBy 的列，明细数据将按照该列的值分片；没有指定 ShardBy 的列,则默
认将根据所有列中的数据进行分片:选择适当的 ShardBy 列,可以使明细数据较为均匀
的分散在多个数据片上，提高并行性，进而获得更理想的查询效率；
   建议选择基数较大的列作为 ShardBy 列，以避免数据分散不均匀。